﻿using System;
using System.Collections.Generic;
using BlobNet.Base.Enums;
using BlobNet.Base.Storage;
using BlobNet.TestsCommon.UnitTests.Dummy;
using BlobNet.TestsCommon.Utils;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Impl;

namespace BlobNet.Base.Tests.UnitTests.BlobPartObjectBaseTests
{
    //TODO: refactor tests
    //TODO: coverage 100%

    [TestFixture]
    [Category("Unit Tests")]
    public class BlobPartTests
    {
        [TestFixtureSetUp]
        public void StartFixture()
        {
            RhinoMocks.Logger = new TextWriterExpectationLogger(Console.Out);
        }


        [Test]
        public void Can_Create_BlobPart()
        {
            //Arrange
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain());

            //assert
            Assert.IsTrue(target.StatusStorage == StatusStorageType.Memory);
        }

       

        [Test]
        public void Can_Create_BlobPart_Without_BlobDomain()
        {
            //Arrange
            var target = new DummyBlobPart();

            //assert
            Assert.IsTrue(target.StatusStorage == StatusStorageType.Memory);
        }

        [Test]
        public void Can_MoveStorage_ToDb()
        {
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain());

            Assert.IsTrue(target.StatusStorage == StatusStorageType.Memory);

            target.StatusStorage = StatusStorageType.Persistent;

        }

        [Test]
        public void Can_Restore_BlobPart_From_DataProvider()
        {
            //Arrange
            IStorageManager memPersistor;
            IStorageManager dbPersistor;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memPersistor, out dbPersistor);
            memPersistor.Stub(x => x.CanDettach()).Return(true);
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var target = new DummyBlobPart(blobDomain, factory);

            //Act
            target.Load(StatusStorageType.Persistent, blobDomain);

            //Assert
            Assert.IsTrue(target.EntityId != Guid.Empty);
            Assert.IsTrue(target.StatusStorage == StatusStorageType.Persistent);
            Assert.AreSame(target.CurrentStorage, dbPersistor);
            Assert.AreEqual(target.StorageFactory, factory);
            Assert.IsFalse(target.BlobDomain == null);
        }

        [Test]
        [Ignore]
        public void Can_Retrieve_ListType_BlobPartDb()
        {
            Assert.Inconclusive();
        }

        [Test]
        public void Can_Retrieve_ListType_BlobPartMemory()
        {
            //Arrange
            IStorageManager memPersistor;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memPersistor);
            memPersistor.Stub(x => x.CanDettach()).Return(true);
            var expected = new List<DummyBlobPart>
                               {new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory)};
            memPersistor.Stub(x => x.GetStatusValue<IList<DummyBlobPart>>(Arg<string>.Is.Anything)).Return(
                expected);
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);

            //Act
            var actual = target.GetStatusValue<IList<DummyBlobPart>>("TestProperty");

            //Assert
            memPersistor.AssertWasCalled(x => x.GetStatusValue<IList<DummyBlobPart>>("TestProperty"));

            Assert.IsTrue(actual == expected);
        }

        [Test]
        public void Can_Retrieve_Property_BlobPartDb()
        {
            //Arrange
            string expected = "Test";

            var memPersistor = MockRepository.GenerateStub<IStorageManager>();
            memPersistor.Stub(x => x.CanDettach()).Return(true);
            memPersistor.Stub(x => x.GetStatusValue<string>(Arg<string>.Is.Anything)).Return(expected);

            var factory = MockRepository.GenerateStub<IStorageManagerFactory>();

            factory.Stub(
                x =>
                x.CreateStorage(Arg<StatusStorageType>.Is.Equal(StatusStorageType.Memory),
                                Arg<Guid>.Is.Anything, Arg<BlobDomain>.Is.Anything)).Return(memPersistor);

            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);

            //Act
            var actual = target.GetStatusValue<string>("StringProperty");

            //Assert
            memPersistor.AssertWasCalled(x => x.GetStatusValue<string>("StringProperty"));
            Assert.IsTrue(actual == expected);
        }

        [Test]
        public void Can_Retrieve_Property_BlobPartMemory()
        {
            //Arrange
            var memPersistor = MockRepository.GenerateMock<IStorageManager>();
            string expected = "Test";
            memPersistor.Stub(x => x.GetStatusValue<string>(Arg<string>.Is.Anything)).Return(expected);
            memPersistor.Stub(x => x.CanDettach()).Return(true);

            var factory = MockRepository.GenerateStub<IStorageManagerFactory>();
            factory.Stub(
                x =>
                x.CreateStorage(Arg<StatusStorageType>.Is.Equal(StatusStorageType.Memory),
                                Arg<Guid>.Is.Anything, Arg<BlobDomain>.Is.Anything)).Return(memPersistor);
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);

            //Act
            var actual = target.GetStatusValue<string>("StringProperty");

            //Assert
            memPersistor.AssertWasCalled(x => x.GetStatusValue<string>("StringProperty"));

            Assert.IsTrue(actual == expected);
        }

        [Test]
        public void Can_SaveOnlyAcceptedTypes()
        {
            var acceptedTypes = new Dictionary<Type, object>();
            acceptedTypes.Add(typeof (byte[]), new byte[2]);
            acceptedTypes.Add(typeof (int), 2);
            acceptedTypes.Add(typeof (DummyBlobPart), new DummyBlobPart());

            var notAcceptedTypes = new Dictionary<Type, object>();
            notAcceptedTypes.Add(typeof (object[]), new object[2]);
            notAcceptedTypes.Add(typeof (object), new object());

            var target = new DummyBlobPart();
            foreach (object acceptedType in acceptedTypes.Values)
            {
                Assert.DoesNotThrow(() => target.SetStatusValue("Test", acceptedType));
            }

            foreach (object nat in notAcceptedTypes.Values)
            {
                Assert.Throws(typeof (NotSupportedException), () => target.SetStatusValue("Test", nat));
            }
        }

        [Test]
        [Ignore]
        public void Can_Save_ListType_BlobPartDb()
        {
            Assert.Inconclusive();
        }

        [Test]
        public void Can_Save_ListType_BlobPartMemory()
        {
            //Arrange
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            IStorageManager memPersistor;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memPersistor);
            memPersistor.Stub(x => x.CanDettach()).Return(true);
            var target = new DummyBlobPart(blobDomain, factory);
            var other = new DummyBlobPart(blobDomain, factory);
            var lstProp = new List<DummyBlobPart>();
            lstProp.Add(other);

            //Act
            target.SetStatusValue("Aggregated", lstProp);

            //Assert
            memPersistor.AssertWasCalled(x => x.SetStatusValue("Aggregated", lstProp));
        }

        [Test]
        public void Can_Save_ListType_BlobPartMemory_When_Is_Not_BlobPart()
        {
            //Arrange
            IStorageManager memPersistor;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memPersistor);
            memPersistor.Stub(x => x.CanDettach()).Return(true);
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);
            var other = new Object();
            var lstProp = new List<object> {other};

            //Act
            target.SetStatusValue("SampleProperty", lstProp);

            //Assert
            memPersistor.AssertWasCalled(x => x.SetStatusValue("SampleProperty", lstProp));
        }


        [Test]
        public void Can_Save_Property_BlobPartDb()
        {
            //TODO: MOVE THIS CODE TO A MORE REUSABLE PLACE

            //Arrange
            IStorageManager memPersistor, dbPersistor;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memPersistor, out dbPersistor);
            memPersistor.Stub(x => x.CanDettach()).Return(true);
            dbPersistor.Stub(x => x.CanDettach()).Return(true);
            string expected = "Test";
            dbPersistor.Stub(x => x.GetStatusValue<string>(Arg<string>.Is.Anything)).Return(expected);
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();
            var target = new DummyBlobPart(blobDomain, factory);
            target.StatusStorage = StatusStorageType.Persistent;
            string stringProperty = "String sample";

            //Act
            target.SetStatusValue("StringProperty", stringProperty);

            //Assert
            dbPersistor.AssertWasCalled(x => x.SetStatusValue("StringProperty", stringProperty));
        }

        [Test]
        public void Can_Save_Property_BlobPartMemory()
        {
            //Arrange
            IStorageManager memPersistor, dbPersistor;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memPersistor, out dbPersistor);

            memPersistor.Stub(x => x.CanDettach()).Return(true);

            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);

            string stringProperty = "String sample";

            //Act
            target.SetStatusValue("StringProperty", stringProperty);

            //Assert
            memPersistor.AssertWasCalled(x => x.SetStatusValue("StringProperty", stringProperty));
        }

        [Test]
        public void Cant_Restore_BlobPart_From_Memory_or_None()
        {
            BlobDomain blobDomain = TestsHelper.GetDummyBlobDomain();

            var target = new DummyBlobPart(blobDomain);

            Assert.Throws(typeof (ArgumentOutOfRangeException),
                          () => target.Load(StatusStorageType.None, blobDomain));
            Assert.Throws(typeof (ArgumentOutOfRangeException),
                          () => target.Load(StatusStorageType.Memory, blobDomain));
        }


        [Test]
        public void Cant_Save_Property_With_InvalidCharacters()
        {
            //Arrange
            IStorageManager memProvider;
            IStorageManager dbProvider;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memProvider, out dbProvider);
            memProvider.Stub(x => x.CanDettach()).Return(true);
            dbProvider.Stub(x => x.CanDettach()).Return(true);
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);

            string stringProperty = "String sample";

            var invalidCharsTested = new[] {' ', '@', '\\', '/', '-', '%', '<', '>', '*', '+'};

            foreach (char c in invalidCharsTested)
            {
                Assert.Throws(typeof (ArgumentException),
                              () => target.SetStatusValue(string.Format("String{0}Property", c), stringProperty),
                              string.Format("Property with char {0} shouldn't be accepted as a name", c));
            }
        }

        [Test]
        public void Cant_StoreInDb_BlobPart_Without_BlobDomain()
        {
            var target = new DummyBlobPart();

            Assert.Throws(typeof (InvalidOperationException), () => target.StatusStorage = StatusStorageType.Persistent);
        }

        [Test]
        public void Save_EmptyPropertyName_ThrowsException()
        {
            //Arrange
            IStorageManager memProvider;
            IStorageManager dbProvider;
            IStorageManagerFactory factory = TestsHelper.GetStubFactory(out memProvider, out dbProvider);
            memProvider.Stub(x => x.CanDettach()).Return(true);
            dbProvider.Stub(x => x.CanDettach()).Return(true);
            var target = new DummyBlobPart(TestsHelper.GetDummyBlobDomain(), factory);
            string stringProperty = "Test";

            //Act-Assert
            Assert.Throws(typeof (ArgumentException), () => target.SetStatusValue(string.Empty, stringProperty));
        }
    }
}